package com.example.demo07cache.controller;

import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Objects;

/**
 * @author wangxun
 */
@RestController
@RequestMapping("/redisson")
public class RedissonController implements InitializingBean {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private Redisson redisson;

    @Autowired
    private RBloomFilter<Object> bloomFilter;

    /**
     * 将数据库中的数据加载到布隆过滤器
     * 布隆过滤器的缺点: 过滤器中的数据不能删除，如果数据库中的数据和布隆过滤器中的数据差距大的话，需要定期重建
     * 如何解决布隆过滤器的缺点: 使用布谷鸟过滤器，他可以有删除的操作，缺点: BitMap 的扩容必须是二次幂增长，不能渐进式扩容
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        for (int i = 1; i <= 100; i++) {
            bloomFilter.add("user:" + i);
        }
    }

    @GetMapping("/luck")
    public String deductStock01() {
        String lockKey = "product_001";

        RLock redLock = redisson.getLock(lockKey);

        redLock.lock();
        try {
            // 2.get key
            int stock = Integer.parseInt((String) Objects.requireNonNull(redisTemplate.opsForValue().get("stock")));
            if (stock > 0) {
                int realStock = stock - 1;
                // 3.set key value
                redisTemplate.opsForValue().set("stock", String.valueOf(realStock));
                System.out.println("扣减成功，剩余库存: " + realStock);
            } else {
                System.out.println("扣减失败，库存不足");
            }
        } finally {
            // 是锁定的 && 锁被当前线程所持有
            if (redLock.isLocked() && redLock.isHeldByCurrentThread()) {
                redLock.unlock();
            }
        }
        return "end";
    }

    /**
     * 双写不一致问题怎么解决？ 更新完数据库就删除缓存，在设置缓存的时候加过期时间
     * 但是读写并发不一致问题怎么解决？ 读写锁
     * @return
     */
    @GetMapping("/readWriteLock")
    public String readWriteLock() {
        String lockKey = "";
        RReadWriteLock readWriteLock = redisson.getReadWriteLock(lockKey);
        RLock readLock = readWriteLock.readLock();
        RLock writeLock = readWriteLock.writeLock();
        return null;
    }

    @GetMapping("/bloomFilter/{key}")
    public boolean bloomFilter(@PathVariable String key) {
        return bloomFilter.contains(key);
    }
}
